#ifndef CORE_LOGGER_H_
#define CORE_LOGGER_H_

#include <QString>
#include <iostream>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>

#include "error_code.h"

#define INFO CLogger::ELoggerLevel_INFO
#define WARN CLogger::ELoggerLevel_WARN
#define ERROR CLogger::ELoggerLevel_ERROR

#define LOG(LEVEL) CLogger(CLogger::ELoggerType_LOG, LEVEL).log()
#define MSG(LEVEL) CLogger(CLogger::ELoggerType_MSG, LEVEL).log()

class ILoggerObserver
{
public:
    ILoggerObserver() = default;
    ~ILoggerObserver() = default;

    virtual void notify(int level, const std::string &msg) {}
};

class CLogger
{
public:
    enum ELoggerLevel
    {
        ELoggerLevel_INFO = 0,
        ELoggerLevel_WARN = 1,
        ELoggerLevel_ERROR = 2
    };

    enum ELoggerType
    {
        ELoggerType_LOG = 0,
        ELoggerType_MSG = 1
    };

    CLogger(int type, int level) :
        type_(type), level_(level), oss_(str_)
    {
    }

    ~CLogger()
    {
        if (type_ == ELoggerType_MSG)
        {
            notify_all(level_, oss_.str());
        }
        else
        {
            print_message(level_, oss_.str());
        }
    }

    std::ostringstream &log() { return oss_; }

    static void add_observer(ILoggerObserver *obs)
    {
        std::unique_lock<std::mutex> ul(_mutex);
        _obs.push_back(obs);
    }

protected:
    static void notify_all(int level, const std::string &msg)
    {
        std::unique_lock<std::mutex> ul(_mutex);
        for (auto &obs : _obs)
        {
            if (obs != nullptr)
            {
                obs->notify(level, msg);
            }
        }
    }
    static void print_message(int level, const std::string &msg)
    {
        auto pfunc = [](int level) -> std::string {
            switch (level)
            {
                case ELoggerLevel_INFO:
                    return "INFO";
                case ELoggerLevel_WARN:
                    return "WARN";
                case ELoggerLevel_ERROR:
                    return "ERRO";
                default:
                    return "LEVEL";
            }
        };

        std::cout << "[" << pfunc(level) << "] " << msg << std::endl;
    }

private:
    int type_;
    int level_;
    std::string str_;
    std::ostringstream oss_;

    static std::mutex _mutex;
    static std::vector<ILoggerObserver *> _obs;
};

std::ostringstream &operator<<(std::ostringstream &stream, const QString &msg);
std::ostringstream &operator<<(std::ostringstream &stream, const CRet &ret);

#endif  // CORE_LOGGER_H_